package top.amot.forceview

import android.content.Context
import android.graphics.Canvas
import android.graphics.PixelFormat
import android.util.AttributeSet
import android.view.*
import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener
import timber.log.Timber
import top.amot.forceview.force.ForceCenter
import top.amot.forceview.layout.I_Layout
import top.amot.forceview.model.AbstractNodeModel
import top.amot.forceview.model.Node.Companion.RADIUS
import top.amot.forceview.theme.DefaultTheme
import top.amot.forceview.theme.I_Theme
import java.util.concurrent.atomic.AtomicBoolean

class ForceView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : SurfaceView(context, attrs, defStyleAttr), SurfaceHolder.Callback, Simulation.Callback {
    private var onNodeClickListener: OnNodeClickListener? = null
    private lateinit var surfaceHolder: SurfaceHolder
    private var simulation: Simulation?= null

    private val isDrawAble = AtomicBoolean(false)
    private val isDrawing = AtomicBoolean(false)

    private var translateX = 0f
    private var translateY = 0f

    private var scale = 1f
    private var touchSlop = 0

    private lateinit var theme: I_Theme
    private lateinit var layout: I_Layout
    private lateinit var nodeModel: AbstractNodeModel
    private lateinit var drawer: ForceDrawer

    private lateinit var scaleDetector: ScaleGestureDetector
    private lateinit var gestureListener: GestureDetector.SimpleOnGestureListener
    private lateinit var gestureDetector:GestureDetector

    init {
        init(context)
    }

    private fun init(context: Context) {
        drawer = ForceDrawer(context)
        surfaceHolder = holder
        surfaceHolder.addCallback(this)
        surfaceHolder.setFormat(PixelFormat.TRANSPARENT)
        isFocusable = true
        isFocusableInTouchMode = true
        scaleDetector = ScaleGestureDetector(context, ScaleListener())
        gestureListener=object:GestureDetector.SimpleOnGestureListener(){
            override fun onDown(e: MotionEvent): Boolean {
                val nodeX=(e.x - translateX) / scale
                val nodeY=(e.y - translateY) / scale
                drawer.selectedNode = layout.find(
                    nodeX,
                    nodeY,
                    RADIUS
                )

                Timber.d("GestureDetector onDown :x->${e.x},y->${e.y},nodeX:$nodeX,nodeY:$nodeY,scale->${scale},selectNode :${drawer.selectedNode}")

                return true
            }

            override fun onShowPress(e: MotionEvent) {
                Timber.d("GestureDetector onShowPress :x->${e.x},y->${e.y},selectNode :${drawer.selectedNode}")
                super.onShowPress(e)
            }

            override fun onSingleTapUp(e: MotionEvent): Boolean {
                Timber.d("GestureDetector onSingleTapUp ::x->${e.x},y->${e.y},selectNode :${drawer.selectedNode}")
                drawer.selectedNode?.let {
                    onNodeClickListener?.onNodeClick(it)
                    /*if (abs((newX - x0) * (newY- y0)) < touchSlop * touchSlop) {
                        onNodeClickListener?.onNodeClick(it)
                    }*/
                }
                drawer.selectedNode = null
                onTick()
                return true
            }

            override fun onScroll(
                e1: MotionEvent?,
                e2: MotionEvent,
                distanceX: Float,
                distanceY: Float
            ): Boolean {

                Timber.d("GestureDetector onScroll :distanceX->${distanceX},distanceY->$distanceY,selectNode :${drawer.selectedNode}")

                if(drawer.selectedNode == null) {
                    translateX -= distanceX / scale
                    translateY -= distanceY / scale
                    onTick()
                }else {
                    drawer.selectedNode?.let {
                        it.x -= distanceX / scale
                        it.y -= distanceY / scale
                        simulation?.restart()
                        //onTick()
                    }
                }
                return true
            }

            override fun onLongPress(e: MotionEvent) {
                Timber.d("GestureDetector onLongPress ::x->${e.x},y->${e.y},selectNode :${drawer.selectedNode}")
                super.onLongPress(e)
            }

            override fun onFling(
                e1: MotionEvent?,
                e2: MotionEvent,
                velocityX: Float,
                velocityY: Float
            ): Boolean {
                Timber.d("GestureDetector onFling :distanceX->${velocityX},distanceY->$velocityY,selectNode :${drawer.selectedNode}")
                /*if(drawer.selectedNode == null) {
                    translateX -= velocityX / scale
                    translateY -= velocityY / scale
                    onTick()
                }*/
                return true
            }

            override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
                Timber.d("GestureDetector onSingleTapConfirmed ::x->${e.x},y->${e.y},selectNode :${drawer.selectedNode}")

                return super.onSingleTapConfirmed(e)
            }

            override fun onDoubleTap(e: MotionEvent): Boolean {

                Timber.d("GestureDetector onDoubleTap ::x->${e.x},y->${e.y},selectNode :${drawer.selectedNode}")
                translateX = 0f
                translateY = 0f
                scale = 1f
                drawer.selectedNode=null
                onTick()
                return true
            }

            override fun onDoubleTapEvent(e: MotionEvent): Boolean {
                Timber.d("GestureDetector onDoubleTapEvent ::x->${e.x},y->${e.y},selectNode :${drawer.selectedNode}")
                return super.onDoubleTapEvent(e)
            }

            override fun onContextClick(e: MotionEvent): Boolean {
                Timber.d("GestureDetector onContextClick ::x->${e.x},y->${e.y},selectNode :${drawer.selectedNode}")
                return super.onContextClick(e)
            }
        }
        gestureDetector= GestureDetector(context,gestureListener)
        touchSlop = ViewConfiguration.get(context).scaledTouchSlop
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        scaleDetector.onTouchEvent(event)
        if(!scaleDetector.isInProgress) {
            gestureDetector.onTouchEvent(event)
        }
        return true
    }


    fun setSimulation(simulation: Simulation) {

        this.simulation?.destroy()

        this.simulation = simulation

        this.theme = simulation.theme?:DefaultTheme(context)
        this.layout = simulation.layout
        this.nodeModel=simulation.nodeModel

        simulation.forces[ForceCenter.NAME]?.let {
            it as ForceCenter
            it.x(width * 0.5f).y(height * 0.5f)
        }

        layout.init(nodeModel,simulation.drawManager,simulation.forces)

        drawer.init(theme,layout,nodeModel,simulation.drawManager)

        simulation.setCallback(this)
        simulation.restart()

        //onTick()
    }

    fun setOnNodeClickListener(listener: OnNodeClickListener?) {
        onNodeClickListener = listener
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        Timber.tag("--force").d("surfaceCreated")
        isDrawAble.set(true)
        isDrawing.set(false)
        simulation?.restart()
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
        Timber.tag("--force").d("surfaceChanged: width=$width , height=$height")
//        simulation?.getForce(ForceCenter.NAME)?.let {
//            it as ForceCenter
//            it.x(width * 0.5f).y(height * 0.5f)
//        }
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        Timber.tag("--force").d("surfaceDestroyed")
        isDrawAble.set(false)
        simulation?.stop()
    }

    override fun onTick() {
        Timber.tag("--force").d("onTick:drawAble=${ isDrawAble.get()} isDrawing=${ isDrawing.get()} ")
        if (isDrawAble.get()&&!isDrawing.get()) {
            isDrawing.set(true)
            var canvas: Canvas? = null
            try {
                canvas = surfaceHolder.lockCanvas()
                drawForce(canvas)
            } catch (e: Exception) {
                Timber.tag("ForceView").e(e)
            } finally {
                if (canvas != null) {
                    surfaceHolder.unlockCanvasAndPost(canvas)
                }
                isDrawing.set(false)
            }
        }
    }

    private fun drawForce(canvas: Canvas) {
        canvas.save()
        canvas.translate(translateX, translateY)
        canvas.scale(scale, scale)
        canvas.drawColor(theme.backgroundColor)
        drawer.drawForce(canvas)
        canvas.restore()
    }

    override fun onEnd() {
        Timber.tag("--force").d("onEnd")
    }

    override fun dispatchTouchEvent(event: MotionEvent):Boolean{
        parent.requestDisallowInterceptTouchEvent(true)
        return super.dispatchTouchEvent(event)
    }


    override fun onDetachedFromWindow() {
        simulation?.destroy()
        super.onDetachedFromWindow()
    }

    private inner class ScaleListener : SimpleOnScaleGestureListener() {
        override fun onScale(detector: ScaleGestureDetector): Boolean {
            Timber.d("GestureDetector onScale inProgress:${detector.isInProgress},scale:${scale},selectNode :${drawer.selectedNode}")

            if (detector.isInProgress) {
                scale *= detector.scaleFactor
                scale = 0.1f.coerceAtLeast(scale.coerceAtMost(5.0f))
                onTick()
            }
            /*if (detector.isInProgress) {
                val factor = detector.scaleFactor
                val pScale = scale
                scale *= factor
                scale = 0.1f.coerceAtLeast(scale.coerceAtMost(5.0f))
                if (!((pScale == 0.1f && scale == 0.1f) || (pScale == 5f && scale == 5f))) {
                    val focusX = detector.focusX
                    val focusY = detector.focusY
                    translateX += (focusX - translateX) * (1 - factor)
                    translateY += (focusY - translateY) * (1 - factor)
                }
                onTick()
            }*/
            return true
        }
    }
}